順序回路と組み合わせ回路を意識した記述を!:触って学ぼう FPGA開発入門(6)(1/4 ページ)
7セグメントLEDを2つ使用して、60進のアップ・ダウンカウンタを作成。“無駄な資源を食わない回路”を設計するには? そのポイントを紹介
連載第5回「階層構造を意識した設計スタイルとは?」では、10進のアップ・ダウンカウンタと7セグメントLEDのデコーダのモジュールをそれぞれインスタンスして接続し、動作させました。
最終回となる今回は、7セグメントLEDを2つ使用して、60進のアップ・ダウンカウンタを作成します。「それほど難しくないだろう」と思われるかもしれませんが、実際に何の情報もないと結構手間取ります。今回の内容は、この連載で一番強調したかった“HDLによるハードウェア設計のエッセンス”が盛り込まれていますので期待してください。
関連記事: | |
---|---|
⇒ | いまさら聞けない FPGA入門 |
ダイナミック点灯の原理
今回使用している「EDX-002」は、ボード上での配線の引き回しを少なくするために、7セグメントLEDの点灯に“ダイナミック点灯”の手法を使用しています。具体的な原理は図1のとおりです。
「点灯させたい7セグメントLEDの選択を行う信号(SA)を出力し、その選択された7セグメントLEDに表示したい値を出力する」というのが基本動作です。また、EDX-002の仕様では選択されていない7セグメントLEDにはハイ・インピーダンスの信号を割り当てることになっています。
この一連の動作をリスト1に示します。
1 module DCOUNT (CLK, ENABLE, L1, L2, L3, L4, SA, L); 2 input CLK, ENABLE; 3 input [7:0] L1, L2, L3, L4; 4 output [3:0] SA; 5 output [7:0] L; 6 7 parameter MAX_COUNT = 3'b111; 8 reg [2:0] sa_count_tmp; 9 reg [3:0] sa_count; 10 reg [7:0] L_tmp; 11 12 assign SA[3] = (sa_count[3]==1'b0)? 1'b0 : 1'bz; 13 assign SA[2] = (sa_count[2]==1'b0)? 1'b0 : 1'bz; 14 assign SA[1] = (sa_count[1]==1'b0)? 1'b0 : 1'bz; 15 assign SA[0] = (sa_count[0]==1'b0)? 1'b0 : 1'bz; 16 assign L = L_tmp; 17 18 always @(posedge CLK) 19 begin 20 if (ENABLE==1'b1) 21 if (sa_count_tmp==MAX_COUNT) 22 sa_count_tmp <= 3'b000; 23 else 24 sa_count_tmp <= sa_count_tmp + 1'b1; 25 end 26 27 always @(posedge CLK) 28 begin 29 if (sa_count_tmp[0]==1'b0) 30 begin 31 sa_count <= 4'b1111;L_tmp <= L_tmp; 32 end 33 else 34 case (sa_count_tmp[2:1]) 35 2'b00:begin 36 sa_count <= 4'b1110;L_tmp <= L4; 37 end 38 2'b01:begin 39 sa_count <= 4'b1101;L_tmp <= L3; 40 end 41 2'b10:begin 42 sa_count <= 4'b1011;L_tmp <= L2; 43 end 44 2'b11:begin 45 sa_count <= 4'b0111;L_tmp <= L1; 46 end 47 default:begin 48 sa_count <= 4'bxxxx;L_tmp <= 8'bxxxxxxxx; 49 end 50 endcase 51 end 52 53 endmodule
リスト1 ダイナミック点灯させるためのモジュール(dcount.v) |
18〜25行目
sa_count_tmpという3bitのカウンタで0〜7までカウント。このときにENABLE信号(kHzオーダーで1回有効になる信号)が‘1’のときにのみカウントアップ
27〜51行目
このカウンタの値が奇数(sa_count_tmp[0]が‘1’)のときに、出力したい7セグメントLEDのデコーダの信号を選択
12〜15行目
選択していない7セグメントLEDにはハイ・インピーダンスを出力
今回の7セグメントLEDの2けたの点灯には、このモジュール(リスト1)を用います。
回路構成の検討
まず、60進カウンタを設計するに当たり、どのようにこのカウンタを構成するのかを考えてみましょう。
単純に考えると「0〜59」、すなわち60回カウントすればいいので、「6bitのカウンタを作成すれば簡単に実現できる! 」と思い付きます。
しかし、最終的に7セグメントLEDに出力するためには、10の位と1の位に6bitのカウンタの値を分けなくてはなりません。
分ける方法はいくつかありますが、ソフトウェア開発が得意な方の場合、
- 1の位:60進カウンタ%10(10で割った余り)
- 10の位:60進カウンタ/10(10で割った商)
で、求めることを思い浮かべるのではないでしょうか? 確かに、アルゴリズム的には正しいのですが、残念ながら上記のような「%」や「/」の使い方は、論理合成ツールがサポートしていません。つまり、回路が作成できないので、RTLではないということになります。ということで、この方法は「使用不可」です。
気を取り直して、さらに考えてみましょう。
次に思い付くのは、「6bitのカウンタの値を10の位と1の位にデコードして分ける」という方法です(リスト2)。
(省略) 8 always @(count60) 9 case (count60) 10 6'd0:{cnt10,cnt1}=7'h0_0; 11 6'd1:{cnt10,cnt1}=7'h0_1; 12 6'd2:{cnt10,cnt1}=7'h0_2; 13 6'd3:{cnt10,cnt1}=7'h0_3; 14 6'd4:{cnt10,cnt1}=7'h0_4; 15 6'd5:{cnt10,cnt1}=7'h0_5; 16 6'd6:{cnt10,cnt1}=7'h0_6; 17 6'd7:{cnt10,cnt1}=7'h0_7; 18 6'd8:{cnt10,cnt1}=7'h0_8; 19 6'd9:{cnt10,cnt1}=7'h0_9; 20 6'd10:{cnt10,cnt1}=7'h1_0; 21 6'd11:{cnt10,cnt1}=7'h1_1; 22 6'd12:{cnt10,cnt1}=7'h1_2; 23 6'd13:{cnt10,cnt1}=7'h1_3; 24 6'd14:{cnt10,cnt1}=7'h1_4; 25 6'd15:{cnt10,cnt1}=7'h1_5; 26 6'd16:{cnt10,cnt1}=7'h1_6; 27 6'd17:{cnt10,cnt1}=7'h1_7; 28 6'd18:{cnt10,cnt1}=7'h1_8; 29 6'd19:{cnt10,cnt1}=7'h1_9; 30 6'd20:{cnt10,cnt1}=7'h2_0; 31 6'd21:{cnt10,cnt1}=7'h2_1; 32 6'd22:{cnt10,cnt1}=7'h2_2; 33 6'd23:{cnt10,cnt1}=7'h2_3; 34 6'd24:{cnt10,cnt1}=7'h2_4; 35 6'd25:{cnt10,cnt1}=7'h2_5; 36 6'd26:{cnt10,cnt1}=7'h2_6; 37 6'd27:{cnt10,cnt1}=7'h2_7; 38 6'd28:{cnt10,cnt1}=7'h2_8; 39 6'd29:{cnt10,cnt1}=7'h2_9; 40 6'd30:{cnt10,cnt1}=7'h3_0; 41 6'd31:{cnt10,cnt1}=7'h3_1; 42 6'd32:{cnt10,cnt1}=7'h3_2; 43 6'd33:{cnt10,cnt1}=7'h3_3; 44 6'd34:{cnt10,cnt1}=7'h3_4; 45 6'd35:{cnt10,cnt1}=7'h3_5; 46 6'd36:{cnt10,cnt1}=7'h3_6; 47 6'd37:{cnt10,cnt1}=7'h3_7; 48 6'd38:{cnt10,cnt1}=7'h3_8; 49 6'd39:{cnt10,cnt1}=7'h3_9; 50 6'd40:{cnt10,cnt1}=7'h4_0; 51 6'd41:{cnt10,cnt1}=7'h4_1; 52 6'd42:{cnt10,cnt1}=7'h4_2; 53 6'd43:{cnt10,cnt1}=7'h4_3; 54 6'd44:{cnt10,cnt1}=7'h4_4; 55 6'd45:{cnt10,cnt1}=7'h4_5; 56 6'd46:{cnt10,cnt1}=7'h4_6; 57 6'd47:{cnt10,cnt1}=7'h4_7; 58 6'd48:{cnt10,cnt1}=7'h4_8; 59 6'd49:{cnt10,cnt1}=7'h4_9; 60 6'd50:{cnt10,cnt1}=7'h5_0; 61 6'd51:{cnt10,cnt1}=7'h5_1; 62 6'd52:{cnt10,cnt1}=7'h5_2; 63 6'd53:{cnt10,cnt1}=7'h5_3; 64 6'd54:{cnt10,cnt1}=7'h5_4; 65 6'd55:{cnt10,cnt1}=7'h5_5; 66 6'd56:{cnt10,cnt1}=7'h5_6; 67 6'd57:{cnt10,cnt1}=7'h5_7; 68 6'd58:{cnt10,cnt1}=7'h5_8; 69 6'd59:{cnt10,cnt1}=7'h5_9; 70 default:{cnt10,cnt1}=7'hx; 71 endcase (省略)
リスト2 10の位と1の位分離デコーダ部分(cnt60_dec.v) |
しかし、この方法では図2のように7セグメントLEDのデコーダの前に1の位と10の位を分けるデコーダが入るので、回路が大きくなり、LEDの出力まで考えるといままでのデコーダ出力よりも遅くなってしまいます(今回のようなLED表示に関しては、本来それほど神経をとがらせる必要はありません)。つまり、今回の場合には60進カウンタを作成するという方法は、あまり得策とはいえません。
そこで、いちいち10の位と1の位を分けるデコーダを作成することがないように、カウンタをあらかじめ「10進カウンタ」と「6進カウンタ」に分けて設計します(図3)。こうすれば、いままでと同様にカウンタの出力をそのまま7セグメントLEDのデコーダに接続できます。
Copyright © ITmedia, Inc. All Rights Reserved.